package cn.boodqian.airreport;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import cn.boodqian.util.GAERequest;
import cn.boodqian.util.Log;
import cn.boodqian.util.ServerMessageTask;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Button;
import android.widget.Toast;
import android.widget.ViewFlipper;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import cn.boodqian.util.ObjectSerializer;

import com.google.ads.*;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

public class AirreportActivity extends Activity {
	class MyGestureDetectorListener extends SimpleOnGestureListener {
		private static final int FLING_MIN_DISTANCE = 30;
		private static final int FLING_MAX_DISTANCE = 200;
		private static final int FLING_THRESHOLD_VELOCITY = 5;

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE
					&& Math.abs(velocityX) > FLING_THRESHOLD_VELOCITY) {
				flipper.setInAnimation(AnimationUtils.loadAnimation(
						AirreportActivity.this, R.anim.slide_right_in));
				flipper.setOutAnimation(AnimationUtils.loadAnimation(
						AirreportActivity.this, R.anim.slide_left_out));
				flipper.showNext();
			} else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE
					&& Math.abs(velocityX) > FLING_THRESHOLD_VELOCITY) {
				flipper.setInAnimation(AnimationUtils.loadAnimation(
						AirreportActivity.this, R.anim.slide_left_in));
				flipper.setOutAnimation(AnimationUtils.loadAnimation(
						AirreportActivity.this, R.anim.slide_right_out));
				flipper.showPrevious();
			}
			return false;
		}
	}
	
	class ServerMessageHandler extends Handler {
		/* (non-Javadoc)
		 * @see android.os.Handler#handleMessage(android.os.Message)
		 */
		@Override
		public void handleMessage(Message msg) {
			long when = System.currentTimeMillis();
			int currentVersion = mGlobalData.getVersionCode();
			int messageVersion = 0;
			int savedLastServerMessageCode = mGlobalData.gLastServerMessageCode;
			long savedLastUpdateMessageTime = mGlobalData.gLastUpdateMessageTime;
			Float lockWeight = 0f;
			try{
				HashMap<String, String> map = (HashMap<String, String>)msg.obj;
				for(String x:map.keySet()) {
					Log.d("Key="+x+", Bytes:"+x.getBytes());
				}
				
				messageVersion = Integer.parseInt(map.get("version"));
				Log.i(String.format("Server Message rcv, %d (%d)", messageVersion, currentVersion));
				
				if(currentVersion>messageVersion) return;
				
				String message = null;
				String title = null;
				int messageCode=0;
				if(currentVersion<messageVersion) {
					// When version below, show 'update' field
					title = AirreportActivity.this.getString(R.string.server_update);
					message = map.get("update");
					
					// Only display update message once a day
					if(isSameDay(mGlobalData.gLastUpdateMessageTime, when)) return;
					mGlobalData.gLastUpdateMessageTime = when;
				} else {
					// When version matches, show 'message' field
					title = AirreportActivity.this.getString(R.string.server_message);
					message = map.get("message");
					String str = map.get("message-code");
					if(str!=null)
						messageCode = Integer.parseInt(str);
					
					Log.i(String.format("message-code: old %d new %d", mGlobalData.gLastServerMessageCode, messageCode));
					
					// Only display message with higher code
					if(mGlobalData.gLastServerMessageCode >= messageCode) return;
					mGlobalData.gLastServerMessageCode = messageCode;
				}
				if(message==null || message.compareTo("")==0) return;
				
				Intent intent = new Intent(AirreportActivity.this, ServerMessageActivity.class);
				intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
				intent.putExtra("title", title);
				intent.putExtra("message", message);
				PendingIntent contentIntent = PendingIntent.getActivity(AirreportActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT );
				Notification notification = new Notification(cn.boodqian.airreport.R.drawable.ic_launcher, title, when);
				notification.flags = Notification.FLAG_AUTO_CANCEL;
				NotificationManager nm = (NotificationManager) AirreportActivity.this.getSystemService(NOTIFICATION_SERVICE);
				notification.setLatestEventInfo(AirreportActivity.this, title, message, contentIntent);

				nm.notify(1, notification);
			} catch (Exception e) {
				if(currentVersion == messageVersion)
					mGlobalData.gLastServerMessageCode = savedLastServerMessageCode;
				else
					mGlobalData.gLastUpdateMessageTime = savedLastUpdateMessageTime;
				e.printStackTrace();
			}
		}
		
	}

	private static final String TAG = "AirreportActivity";;

	private ServerMessageHandler handler = new ServerMessageHandler();
	private int mHistoryMode = 1; // 1 - 24 hour, 2 - 30 days
	
	// Global Data
	private GlobalData mGlobalData = GlobalData.getInstance();
	
	// UI Data
	private ArrayList<HashMap<String, String>> mDetailDataList = null;

	// UI Controllers
	private TextView location_address;
	private TextView location_name;
	private TextView history_view_title;
	private ViewFlipper flipper;
	private HistoryView history_view;
	private ListView list_airdata_detail;

	private GestureDetector mGestureDetector;


	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.flipper);
		
		location_address = (TextView) findViewById(R.id.location_address);
		location_name = (TextView) findViewById(R.id.location_name);
		history_view_title = (TextView) findViewById(R.id.history_view_title);
		history_view = (HistoryView) findViewById(R.id.history_view);
		flipper = (ViewFlipper) findViewById(R.id.flipper);
		list_airdata_detail = (ListView) findViewById(R.id.list_airdata_detail);

		registerForContextMenu(location_name);
		registerForContextMenu(history_view_title);
		/*
		history_view_title.setOnClickListener(
				new View.OnClickListener() {
					
					@Override
					public void onClick(View v) {
						if(mHistoryMode == 1) mHistoryMode = 2;
						else if(mHistoryMode == 2) mHistoryMode = 1;
						
						updateHistoryTitle();
						updateUI();
					}
				}
				);
		*/

		mGestureDetector = new GestureDetector(new MyGestureDetectorListener());
		
		Log.i(Boolean.toString(getIntent().getStringExtra("id")!=null));
		updateAirdata(new Runnable(){
			@Override
			public void run() {
				updateUI();
			}
		});
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo) {
		super.onCreateContextMenu(menu, v, menuInfo);

		if (v == location_name || v == history_view_title) {
			if (mGlobalData.gProfileList == null 
					|| mGlobalData.gProfileList.size() == 0) {
				return;
			}
			int index = 0;
			for(String id:mGlobalData.gProfileList) {
				for( HashMap<String, String> locationInfo : mGlobalData.gLocationList) {
					if( id.compareTo(locationInfo.get("id")) == 0 ) {
						menu.add(0, index, 0, mGlobalData.gLocationList.get(index).get("name").toString());
						index++;
					}
				}
			}
		} else {
			MenuInflater inflater = getMenuInflater();
			inflater.inflate(R.menu.data_context, menu);
		}
	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		Log.i("itemid="+item.getItemId());
		switch (item.getItemId()) {
		case R.id.menu_refresh:
			//updateAll();
			break;
		case R.id.menu_history:
			flipper.setDisplayedChild(1);
			break;
		default:
			return super.onContextItemSelected(item);
		}
		return true;
	}


	private Float calcAverage(List<Float> list, int index, int length) {
		assert(index+length-1<list.size());
		float sum = 0.0f;
		int num = 0;
		for(int i=0;i<length;i++) {
			float value = list.get(index+i);
			if (Float.compare(value, 0f) > 0) {
				sum += value;
				num++;
			}
		}
		return num>0 ? (sum/num) : 0f;
	}
	
	private void calc24HourAverage() {
	}

	/**
	 * Name: updateAirdata
	 * 
	 * Parameter: A runnable to run after we get the airdata
	 * 
	 * Purpose: Retrieve and update the airdata of current location
	 * 
	 * Assumption: mLocations and mCurrentLocation has value
	 * 
	 * Description: if mLocations is out of date (return error from server)
	 * caller need to call getLocations again to update
	 */
	// TODO if mLocations is out of date (return error from server) caller need
	// to call getLocations again to update
	private void updateAirdata(final Runnable run) {
		Log.i("enter");
		
		String id = this.getIntent().getStringExtra("id");
		mGlobalData.updateAirdata(new String[] {id}, run);
	}

	private void updateAQIText(int viewId, float value, int aqi) {
		TextView t = (TextView) findViewById(viewId);
		if (value < 1e-10) {
			t.setText(getString(R.string.not_available));
			t.setTextColor(Color.GRAY);
		} else {
			t.setText(String.format("%.3f/%03d:%s", value, aqi,
					getString(AQI.AQICategory(aqi))));
			t.setTextColor(Color.parseColor(AQI.AQIColor(aqi)));
		}
	}

	/**
	 * Name: updateUI Purpose: updateUI according to the data in mAirdataHash
	 * Assumption: None (airdata of current location can be null)
	 */
	private void updateUI() {
		String id = getIntent().getStringExtra("id");
		Log.i(Boolean.toString(id!=null));
		Log.i(Boolean.toString(mGlobalData!=null));
		Log.i(Boolean.toString(mGlobalData.gAirdataHash!=null));
		AirData airdata = mGlobalData.gAirdataHash.get(id);
		
		assert( airdata != null );
		
		HashMap<String, String> locationInfo = mGlobalData.getLocationInfo(id);
		
		location_address.setText(locationInfo.get("address"));
		location_name.setText(locationInfo.get("name"));
		
		int titleid = (mHistoryMode == 1)?R.string.history:R.string.history_30days;
		history_view_title.setText(String.format("%s(%s)",
				getString(titleid), locationInfo.get("name")));
		
		mDetailDataList = new ArrayList<HashMap<String,String>>();
		
		for(String name : GlobalData.gItemNameList) {
			HashMap<String,String> map = new HashMap<String, String>();
			List<Float>data = airdata.getData(name);
			if( data != null && data.size()>0) {
				map.put("name", name);
				map.put("realtime", String.format("%.3f", data.get(data.size()-1)));
				map.put("avg24", String.format("%.3f", calcAverage(data, 0, data.size())));
				mDetailDataList.add(map);
			}
		}
		
		SimpleAdapter adapter = new SimpleAdapter(this, mDetailDataList,
				R.layout.airdata_detail_item, new String[]{"name", "realtime", "avg24"},
				new int[]{R.id.text_dataitem, R.id.text_realtime_value, R.id.text_avg24_value});
		list_airdata_detail.setAdapter(adapter);
		
		// Display the curve
		/* http://www.wahart.com.hk/rgb.htm */
//		history_view.clearHistoryData();
//		if(marks.size()>0) history_view.setHorizonMark(marks);
//		history_view.addHistoryData(hist_pm25_us, 0xffCDAF95, "PM2.5(US)"); // CornflowerBlue
//		history_view.addHistoryData(hist_pm25, 0xff6495ED, "PM2.5"); // CornflowerBlue
//		history_view.addHistoryData(hist_pm10, 0xffFFD700, "PM10"); // Gold
//		history_view.addHistoryData(hist_so2, 0xff2E8B57, "SO2"); // SeaGreen
//		history_view.addHistoryData(hist_no2, 0xffcd5c5c, "NO2"); // IndianRed
//		history_view.invalidate();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.airreport, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection
		switch (item.getItemId()) {
//		case R.id.menu_refresh:
//			//updateAll();
//			return true;
//		case R.id.menu_location:
//			openContextMenu(location_name);
//			return true;
//		case R.id.menu_aqilevel:
//			flipper.setDisplayedChild(2);
//			return true;
//		case R.id.menu_history:
//			flipper.setDisplayedChild(1);
//			return true;
//		case R.id.menu_usage:
//			new AlertDialog.Builder(this).setTitle(getString(R.string.usage))
//					.setMessage(getString(R.string.usage_content))
//					.setPositiveButton(getString(R.string.close), null).show();
//			return true;
//		case R.id.menu_about:
//			String content = String.format("%s v%s\n\n%s\n",
//					getString(R.string.app_name),
//					packageInfo==null?"unkown":packageInfo.versionName,
//							getString(R.string.about_content),
//							getString(R.string.data_source));
//			new AlertDialog.Builder(this)
//			.setTitle(getString(R.string.about))
//			.setMessage(content)
//			.setPositiveButton(getString(R.string.close), null)
//			.setIcon(R.drawable.ic_launcher)
//			.setNeutralButton(R.string.feedback, new DialogInterface.OnClickListener() {
//				
//				@Override
//				public void onClick(DialogInterface dialog, int which) {
//					final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
//					String title = String.format(getString(R.string.feedback_title), getString(R.string.app_name), packageInfo.versionName);
//					emailIntent .setType("message/rfc822");
//					//emailIntent .setType("plain/text"); 
//					emailIntent .putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{"boodweb@gmail.com"});     
//					emailIntent .putExtra(android.content.Intent.EXTRA_SUBJECT, title);     
//					//emailIntent .putExtra(android.content.Intent.EXTRA_TEXT, yourBodyText);     
//					startActivity(Intent.createChooser(emailIntent, "Send mail..."));
//				}
//			})
//			.show();
//
//			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return mGestureDetector.onTouchEvent(event);
	}

	@Override
	protected void onStart() {
		super.onStart();
		
		if(false) {
			// For debug on emulator
			//new ServerMessageTask(handler, packageInfo.versionName).execute("http://10.0.2.2:3000/airreport");
		} else {
			// Direct use of the domain cause mojolicio crash (unknown error), access by ip is fine
			// so translate to ip address first
			//new ServerMessageTask(handler, packageInfo.versionName).execute(getString(R.string.update_url));
		}
	}
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev){
		super.dispatchTouchEvent(ev);    
		return mGestureDetector.onTouchEvent(ev); 
	}

	private boolean isSameDay(long a, long b) {
		return (a/86400000 == b/86400000);
	}

	@Override
	protected void onNewIntent(Intent intent) {
		// TODO Auto-generated method stub
		super.onNewIntent(intent);
		
		Log.i(Boolean.toString(intent.getStringExtra("id")==null));
		this.setIntent(intent);
	}
}